/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 *
 * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
 *
 * Contributor(s): 
 *  Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
 *  
 * If you didn't download this code from the following link, you should check
 * if you aren't using an obsolete version: http://www.isqlviewer.com
 */
package org.isqlviewer.swing.outline;

import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.tree.TreePath;

/**
 * TreeTableModel is the model used by a JTreeTable.
 * <p>
 * It extends TreeModel to add methods for getting inforamtion about the set of columns each node in the TreeTableModel
 * may have. Each column, like a column in a TableModel, has a name and a type associated with it. Each node in the
 * TreeTableModel can return a value for each of the columns and set that value if isCellEditable() returns true.
 * 
 * @author Philip Milne
 * @author Scott Violet
 */
public class TreeTableModelAdapter extends AbstractTableModel {

    private static final long serialVersionUID = 1531337763071850568L;

    private JTree tree;
    private OutlineModel treeTableModel;

    public TreeTableModelAdapter(OutlineModel treeTableModel, JTree tree) {

        this.tree = tree;
        this.treeTableModel = treeTableModel;

        tree.addTreeExpansionListener(new TreeExpansionListener() {

            // Don't use fireTableRowsInserted() here; the selection model
            // would get updated twice.
            public void treeExpanded(TreeExpansionEvent event) {

                fireTableDataChanged();
            }

            public void treeCollapsed(TreeExpansionEvent event) {

                fireTableDataChanged();
            }
        });

        // Install a TreeModelListener that can update the table when
        // tree changes. We use delayedFireTableDataChanged as we can
        // not be guaranteed the tree will have finished processing
        // the event before us.
        treeTableModel.addTreeModelListener(new TreeModelListener() {

            public void treeNodesChanged(TreeModelEvent e) {

                delayedFireTableDataChanged();
            }

            public void treeNodesInserted(TreeModelEvent e) {

                delayedFireTableDataChanged();
            }

            public void treeNodesRemoved(TreeModelEvent e) {

                delayedFireTableDataChanged();
            }

            public void treeStructureChanged(TreeModelEvent e) {

                delayedFireTableDataChanged();
            }
        });
    }

    // Wrappers, implementing TableModel interface.

    public int getColumnCount() {

        return treeTableModel.getColumnCount();
    }

    @Override
    public String getColumnName(int column) {

        return treeTableModel.getColumnName(column);
    }

    @Override
    public Class< ? > getColumnClass(int column) {

        return treeTableModel.getColumnClass(column);
    }

    public int getRowCount() {

        return tree.getRowCount();
    }

    protected Object nodeForRow(int row) {

        TreePath treePath = tree.getPathForRow(row);
        if (treePath == null) {
            return null;
        }
        return treePath.getLastPathComponent();
    }

    public Object getValueAt(int row, int column) {

        return treeTableModel.getValueAt(nodeForRow(row), column);
    }

    @Override
    public boolean isCellEditable(int row, int column) {

        return treeTableModel.isCellEditable(nodeForRow(row), column);
    }

    @Override
    public void setValueAt(Object value, int row, int column) {

        treeTableModel.setValueAt(value, nodeForRow(row), column);
    }

    /**
     * Invokes fireTableDataChanged after all the pending events have been processed. SwingUtilities.invokeLater is used
     * to handle this.
     */
    protected void delayedFireTableDataChanged() {

        SwingUtilities.invokeLater(new Runnable() {

            public void run() {

                fireTableDataChanged();
            }
        });
    }
}
